package main;

//import com.sun.org.apache.xalan.internal.xsltc.runtime.Hashtable;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.cojen.classfile.RuntimeClassFile;

// Used to execute the generated class
import java.lang.reflect.Method;
import java.util.Hashtable;
import org.cojen.classfile.CodeBuilder;
import org.cojen.classfile.LocalVariable;
import org.cojen.classfile.MethodInfo;
import org.cojen.classfile.Modifiers;
import org.cojen.classfile.TypeDesc;
import org.cojen.classfile.Attribute;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

class Pair {

  private final Object left;
  private final Object right;

  public Pair(Object left, Object right) {
    this.left = left;
    this.right = right;
  }

  public Object getLeft() { return left; }
  public Object getRight() { return right; }

  @Override
  public int hashCode() { return left.hashCode() ^ right.hashCode(); }

  @Override
  public boolean equals(Object o) {
    if (o == null) return false;
    if (!(o instanceof Pair)) return false;
    Pair pairo = (Pair) o;
    return this.left.equals(pairo.getLeft()) &&
           this.right.equals(pairo.getRight());
  }

}

/**
 *
 * @author zgredzik
 */
public class ServerScripts {
    private static RuntimeClassFile RCF;
    private static MethodInfo MI;
    private static CodeBuilder CB;
    private static Hashtable HTV;
    private static Hashtable HTF;
    private static Class CLAZZ;
    /**
     * Constructor that initiates the local instances of hashtables
     */
    public ServerScripts()
    {
    }
    
    public static void InitiateRCF()
    {
        RCF = new RuntimeClassFile(null, ServerFunctions.class.getName());
        RCF.addDefaultConstructor();
        CLAZZ = ServerFunctions.class;//RCF.defineClass();
        HTV = new Hashtable();
        HTF = new Hashtable();
        Method[] mthds = CLAZZ.getDeclaredMethods();
        for(int i=0; i!=mthds.length; ++i)
        {
            Class[] pt = mthds[i].getParameterTypes();
            TypeDesc[] tda = new TypeDesc[pt.length];
            for(int j=0; j!=pt.length; ++j)
                tda[j] = TypeDesc.forClass(pt[j]);
            HTF.put(mthds[i].getName(), new Pair(TypeDesc.forClass(mthds[i].getReturnType()), tda.clone()));
            System.out.print("Appending method: "+(TypeDesc.forClass(mthds[i].getReturnType()).getFullName())+" " + mthds[i].getName() + " ( ");
            for(int j=0;j!=tda.length;++j)
                System.out.print(tda[j].getFullName());
            System.out.println(" ) ");
        }
        /*Class clazz = RCF.defineClass();
        try {
            Method m = clazz.getMethod("Foo", null);
            m.invoke(null, null);
        } catch (NoSuchMethodException ex) {
            Logger.getLogger(ServerScripts.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SecurityException ex) {
            Logger.getLogger(ServerScripts.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            Logger.getLogger(ServerScripts.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalArgumentException ex) {
            Logger.getLogger(ServerScripts.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvocationTargetException ex) {
            Logger.getLogger(ServerScripts.class.getName()).log(Level.SEVERE, null, ex);
        }//*/
    }
    
    public static void InitiateMI(TypeDesc A_objType, String A_strName, TypeDesc[] A_arrParams)
    {
        System.out.println("Appending method: "+A_objType.getFullName()+" " + A_strName + "("+A_arrParams+")");
        HTF.put(A_strName, new Pair(A_objType, A_arrParams));
        MI = RCF.addMethod(Modifiers.PUBLIC_STATIC, A_strName, A_objType, A_arrParams);
        CB = new CodeBuilder(MI);
        //CB.mapLineNumber(1);
        HTV.clear();
    }
    
    public static void AddAttribute(TypeDesc A_objType, String A_strName)
    {
        RCF.addField(Modifiers.PUBLIC_STATIC, A_strName, A_objType);
    }
    
    public static void AddVariable(TypeDesc A_objType, String A_strName)
    {
        System.out.println("AddVariable: " + A_objType + " " + A_strName);
        HTV.put(A_strName, CB.createLocalVariable(A_strName, A_objType));
    }
    
    public static void StoreVariable(String A_strName)
    {
        System.out.println("StoreVariable: " + A_strName);
        CB.storeLocal((LocalVariable)HTV.get(A_strName));
    }
    
    public static RuntimeClassFile GetRCF()
    {
        return RCF;
    }
    
    public static CodeBuilder GetCB()
    {
        return CB;
    }
    
    public static Class GetCLAZZ()
    {
        return CLAZZ;
    }
    
    public static void AppendReturnStatement()
    {
        CB.returnVoid();
    }
    
    public static void Finalize()
    {
        CLAZZ = RCF.defineClass();
    }
    
    public static void CallMethod(String A_strName)
    {
        try {
            Method m = CLAZZ.getMethod(A_strName, null);
            m.invoke(null, null);
        }
        catch (Exception e) {
           System.out.println(e);
        }
    }
    
    public static void InvokeStatic(String A_strName)
    {
        System.out.println("outACallExpression/InvokeStatic: " + A_strName);
        Pair p = (Pair)HTF.get(A_strName);
        CB.invokeStatic(A_strName, (TypeDesc)p.getLeft(), (TypeDesc[])p.getRight());
    }
    
    public static void LoadLocal(String A_strName)
    {
        System.out.println("caseAIdentifierValue/LoadLocal: "+A_strName);
        CB.loadLocal((LocalVariable)HTV.get(A_strName));
        //A_strName = "Print";
        //Pair p = (Pair)HTF.get(A_strName);
        //CB.invokeStatic(A_strName, (TypeDesc)p.getLeft(), (TypeDesc[])p.getRight());
        //CB.loadConstant(1);
    }
    /**
     * Checks, whether an identifired is specified locally
     * @param A_strKey identifier name
     * @return true if found, false otherwise
     */
    /*public boolean HasIdentifier(String A_strKey)
    {
        return m_Identifiers.containsKey(A_strKey);
    }*/
    
    /**
     * Declares a variable.
     * @param A_strType type of the identifier
     * @param A_strName name of the identifier
     */
    /*public void DeclareIdentifier(String A_strType, String A_strName)
    {
        
    }*/
}
